'''
Created on Jun 21, 2011

@author: Kathy Benitez

The fileSetUp module allows users of the vdart software to create a vocabulary for use
by the software.  The default behavior is to generate a vocabulary covering exactly the
tokens used in the input file.  A brief example is illustrative.

GENDER    RACE    AGE
Male    White    35
Female    White    38
Male    White    42

Resulting Vocab:
(0,0)    35
(0,1)    38
(0,2)    42
(1,0)    Female
(1,1)    Male
(2,0)    White

Resulting Fields:
["AGE", "GENDER", "RACE"]

Options allow specifying the order of the fields, and skipping some fields (so a vocabulary can
be generated from a file that does not have sensitive information such as name removed).

Options also allow for reordering vocabulary items and adding some that do not exist in the 
dataset (for example, above we may wish to have vocabulary items for all ages 35-45, even though
some do not exist in the dataset).

For this vocabulary to interface properly with the remainder of the vdart software, it is 
necessary for the vocabulary to use the same tokens as the census dataset.  The easiest
way to translate the dataset is to find and replace in Excel or from the python prompt, using
dictionaries.  An appendix to the user manual contains informations about the vocabulary
used in the census dataset (provided you have used the provided utility to create the DB).

'''
import txt
import os, csv, argparse, textwrap

class VocabMaker():
    """
    Creates vocabulary.
    """
    def __init__(self, dictReader):
        self.fields = []
        self.dictReader = dictReader
        self.createLists()
    
        
    def createLists(self):
        """
        Harvests the vocabulary items from the input file. 
        """
        first = True
        for row in self.dictReader:
            if first:
                self.fields = self.dictReader.fieldnames
                vocabLists = dict([(f, set([])) for f in self.fields])
                first = False
            for field in self.fields:
                vocabLists[field].add(row[field])    
        self.vocabLists = dict([(f, sorted(list(vocabLists[f]))) for f in vocabLists])
    
    def output(self):
        """
        Generates the vocabulary in the form required by the vdart software
        based on the current state of the vocab lists.
        """
        vocab = {}
        for i in range(len(self.fields)):
            field = self.fields[i]
            list = self.vocabLists[field]
            vocab.update(dict([((i,j),list[j]) for j in range(len(list))]))
        return vocab
    
    def changeFieldRange(self, field, list):
        """
        Updates the field specified by field to the list specified by list.  Order
        matters.
        """
        self.vocabLists[field] = list
    
    def addElement(self, field, *extras):
        """
        Adds the element or elements to the field specified
        """
        self.vocabLists[field].append(extras).sort()
        
    def switchElements(self, field, item1, item2):
        """
        Reorders elements.  Useful for when there are only a few items to be reordered.
        Otherwise, use changeFieldRange()
        """    
        assert item1 in self.vocabLists[field] and item2 in self.vocabLists[field]
        i1 = next((i for i in xrange(len(self.vocabLists[field])) if len.self.vocabLists[field] == item1), None)
        i2 = next((i for i in xrange(len(self.vocabLists[field])) if len.self.vocabLists[field] == item2), None)
        if i1 is not None and i2 is not None:
            self.vocabLists[field][i1] = item2
            self.vocabLists[field][i2] = item1
    
    def setfieldnames(self, fieldnames):
        """
        Used to modify the order of or remove fields from the vocabulary.  Fields not listed
        in the field listing will not be used for evaluating the privacy of the dataset.
        """
        self.fields = fieldnames
        self.vocabLists = [self.vocabLists[field] for field in self.fields]
        
    def removefields(self, fieldnames):
        """
        Used to modify the order of or remove fields from the vocabulary.  Fields not listed
        in the field listing will not be used for evaluating the privacy of the dataset.
        """
        for field in fieldnames:
            try:
                self.fields.remove(field)
                del self.vocabLists[field]
            except ValueError:
                print "No field %s"%field
                    
    def createFileStructure(self, path):
        import pickle
        curpath = os.path.abspath(os.curdir)
        print curpath
        os.mkdir(path)
        pickle.dump(self.output(), open(os.path.normpath(os.path.join(curpath, path, "vocab.pckl")), 'w'))
        pickle.dump({}, open(os.path.join(curpath, path, "prior.pckl"), 'w'))
        pickle.dump(self.fields, open(os.path.join(curpath, path, "fieldnames.pckl"), 'w'))
    
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Please specify what file you are creating your vocabulary from.')
    # Would like filename to be mandatory.
    parser.add_argument('-f', '--filename', dest = 'fileName', default="../data/demographics_test2.csv")
    parser.add_argument('-d', '--delimiter', dest='delimiter', default=",")
    parser.add_argument('-p', '--outputpath', dest='outputPath', default="../data/test2/")
    parser.add_argument('--final', action='store_true')
    parser.add_argument('-m', '--minus', action='append', default=None)

    args = parser.parse_args()
    input = csv.DictReader(open(args.fileName), delimiter=args.delimiter)
    v = VocabMaker(input)
    vocab = v.output()
    if args.final:
            v.createFileStructure(args.outputPath)
    else:
        if args.minus:
            v.removefields(args.minus)
        print textwrap.fill(txt.useString)
        print "\n\n"
        for list in v.vocabLists:
            print list, ":", v.vocabLists[list]

